/*
 * Created on 04.04.2005
 * 
 * TODO To change the template for this generated file go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
package robocup.component.worldmodel;

import robocup.component.WorldModel;
import robocup.component.infotypes.Info;
import robocup.component.infotypes.PlayerSayInfo;
import robocup.component.infotypes.SenseBodyInfo;
import robocup.component.infotypes.VisualInfo;
import robocup.component.speechacts.MessageFactory.MESSAGES;
import robocup.component.util.RingList;

/**
 * This memory stores data over time and is the complement to the
 * {@link ShortTermMemory}.
 * 
 */
public class LongTermMemory {

  /**
   * class for storing informations about a {@link PlayerSayInfo}.
   */
  private class HearData {

    // sender
    private int      from;

    // arrived when?
    private int      cycle;

    // of what type?
    private MESSAGES mType;

    /**
     * Constructor.
     * 
     * @param cycle -
     *          the when the message arrived
     * @param from -
     *          heared player
     * @param type -
     *          message type
     */
    public HearData(
        final int cycle,
        final int from,
        final MESSAGES type) {

      this.cycle = cycle;
      this.from = from;
      this.mType = type;
    }

    /**
     * @return Returns the from.
     */
    public int getFrom() {

      return this.from;
    }

    /**
     * @param from
     *          The from to set.
     */
    public void setFrom(final int from) {

      this.from = from;
    }

    /**
     * @return Returns the type.
     */
    public MESSAGES getType() {

      return this.mType;
    }

    /**
     * @param type
     *          The type to set.
     */
    public void setType(final MESSAGES type) {

      this.mType = type;
    }

    /**
     * @return Returns the cycle.
     */
    public int getCycle() {

      return this.cycle;
    }

    /**
     * @param cycle
     *          The cycle to set.
     */
    public void setCycle(final int cycle) {

      this.cycle = cycle;
    }
  }

  // length of RingLists (number of cycles to be stored)
  private final int                memoryLength       = 30;

  // arrival times of VI Infos (Time taken at UDP arrival)
  private final RingList<Long>     arrivalTimesVI     = new RingList<Long>(
                                                    this.memoryLength);

  // arrival times of BS Infos (Time taken at UDP arrival)
  private final RingList<Long>     arrivalTimesBS     = new RingList<Long>(
                                                    this.memoryLength);

  // the length of the last cycles
  private final RingList<Long>     cycleLength        = new RingList<Long>(
                                                    this.memoryLength);

  // time between vi-arrival and sending of the resulting action (45°)
  private final RingList<Long>     thinkTimes45       = new RingList<Long>(
                                                    this.memoryLength);

  // think times when the player has the ball
  private final RingList<Long>     thinkTimesWithBall = new RingList<Long>(
                                                    this.memoryLength);

  // last heared messages
  private final RingList<HearData> hearMessages       = new RingList<HearData>(
                                                    this.memoryLength);

  // the world
  private WorldModel         world;

  /**
   * Constructor - a class stroing long-term information within a certain time
   * using <code>RingList</code>s.
   * 
   * @param world -
   *          the current world-model
   */
  public LongTermMemory(
      final WorldModel world) {

    this.world = world;
  }

  /**
   * Remember arriving infos. If it was a {@link PlayerSayInfo} store the
   * message data.
   * 
   * @param info -
   *          the info
   */
  public void insertInfo(final Info info) {

    if (info.getInfoType() == Info.TYPE_VISUAL) {
      this.insertInfo((VisualInfo) info);
    }
    else if (info.getInfoType() == Info.TYPE_SENSE) {
      this.insertInfo((SenseBodyInfo) info);
    }
    else if (info.getInfoType() == Info.TYPE_AURAL) {
      // insertInfo((AuralInfo)info);
    }
    else if (info.getInfoType() == Info.TYPE_AURAL_PSI) {
      final PlayerSayInfo psi = (PlayerSayInfo) info;
      final char c = psi.getMessage().charAt(0);
      final MESSAGES m = (MESSAGES.values())[this.world.getMessageFactory().valuesOfChars[c]];
      final HearData hearData = new HearData(psi.getCycle(), psi.getNumber(), m);
      this.hearMessages.insert(hearData);
    }
  }

  /**
   * Called after info arrival in wm.
   * 
   * @param _info
   *          the info that arrived.
   */
  public void insertInfo(final SenseBodyInfo _info) {

    if (this.arrivalTimesBS.length() > 0) {
      this.cycleLength.insert(_info.getArrivalTime() - this.arrivalTimesBS
          .getLast());
    }
    this.arrivalTimesBS.insert(_info.getArrivalTime());
  }

  /**
   * Called after info arrival in wm.
   * 
   * @param _info
   *          the info that arrived.
   */
  public void insertInfo(final VisualInfo _info) {

    this.arrivalTimesVI.insert(_info.getArrivalTime());
  }

  /**
   * The last SenseBodyInfo. null if empty
   * 
   * @return the last SenseBodyInfo, null if empty
   */
  public long getLastBSArrivalTime() {

    return this.arrivalTimesBS.getLast();
  }

  /**
   * The last VisualInfo. null if empty
   * 
   * @return the last VisualInfo, null if empty
   */
  public long getLastVIArrivalTime() {

    return this.arrivalTimesVI.getLast();
  }

  /**
   * The last SenseBody info arrival times in ms.
   * 
   * @return {@link RingList} containing the last SenseBody info arrival times
   *         in ms.
   */
  public RingList<Long> getBSArrivalTimes() {

    return this.arrivalTimesBS;
  }

  /**
   * The last visual info arrival times in ms.
   * 
   * @return {@link RingList} containing the last visual info arrival times in
   *         ms.
   */
  public RingList<Long> getVIArrivalTimes() {

    return this.arrivalTimesVI;
  }

  /**
   * The arrival time for a BSInfo given by index.
   * 
   * @param i -
   *          the index (0 = this cycle, 1 = last cyle, etc.)
   * @return the arrival time in ms
   */
  public long getLastBSArrivalTime(final int i) {

    return this.arrivalTimesBS.getLast(i);
  }

  /**
   * The arrival time for a VisualInfo given by index.
   * 
   * @param i -
   *          the index (0 = this cycle, 1 = last cyle, etc.)
   * @return the arrival time in ms
   */
  public long getLastVIArrivalTime(final int i) {

    return this.arrivalTimesVI.getLast(i);
  }

  /**
   * @return Returns a {@link RingList} containing the cycle Length.
   */
  public RingList<Long> getCycleLength() {

    return this.cycleLength;
  }

  /**
   * @return Returns a {@link RingList} containg the thinkTimes45 in ms.
   */
  public RingList<Long> getThinkTimes45() {

    return this.thinkTimes45;
  }

  /**
   * @return Returns a {@link RingList} containing all thinkTimesWithBall.
   */
  public RingList<Long> getThinkTimesWithBall() {

    return this.thinkTimesWithBall;
  }

  /**
   * The sender of a message.
   * 
   * @param cycleDiff -
   *          cycleDiff of the message' arrival (0 = this cycle, 1 = last cycle,
   *          etc.)
   * @return the player heared in this cycle
   */
  public int getHearedMessageSender(final int cycleDiff) {

    if (cycleDiff > this.hearMessages.length() - 1) {
      return -1;
    }
    return this.hearMessages.getLast(cycleDiff).getFrom();
  }

  /**
   * The message type of a given message.
   * 
   * @param cycleDiff -
   *          cycleDiff of the message' arrival (0 = this cycle, 1 = last cycle,
   *          etc.)
   * @return the type of the message heared in this cycle
   */
  public MESSAGES getHearedMessageType(final int cycleDiff) {

    if (cycleDiff > this.hearMessages.length() - 1) {
      return null;
    }
    return this.hearMessages.getLast(cycleDiff).getType();
  }

}
